package com.navinfo.platform.trip.analysis.service.impl.common;

import com.navinfo.platform.trip.analysis.pojo.AlgorithmChainData;
import com.navinfo.platform.trip.analysis.service.AlgorithmChainService;
import com.navinfo.platform.trip.analysis.util.TripStatisticUtils;
import com.navinfo.platform.trip.common.arithmetic.FuelLeakLocation_0200;
import com.navinfo.platform.trip.common.arithmetic.Parking_0200;
import com.navinfo.platform.trip.common.arithmetic.SpeedOverCurve_0200;
import com.navinfo.platform.trip.common.arithmetic.SpeedOver_0200;
import com.navinfo.platform.trip.common.arithmetic.data.CommonData;
import com.navinfo.platform.trip.common.arithmetic.data.DrivingData;
import com.navinfo.platform.trip.common.arithmetic.manager.ArithmeticManager;
import com.navinfo.platform.trip.common.arithmetic.manager.IArithmetic;
import com.navinfo.platform.trip.common.config.ConfigMap;
import com.navinfo.platform.trip.common.enums.ProtocolEnum;
import com.navinfo.platform.trip.common.fuelleak.FuelLeakLocation;
import com.navinfo.platform.trip.common.fuelleak.FuelLeakService;
import com.navinfo.platform.trip.common.fuelleak.FuelLeakServiceImpl;
import com.navinfo.platform.trip.common.pojo.PointProtocol;
import com.navinfo.platform.trip.common.util.CommonUtils;
import com.navinfo.platform.trip.common.util.DateUtils;
import com.navinfo.platform.trip.common.util.TileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 流计算使用
 * 使用位置点0200数据进行的事件收集算法链<br/>
 * 只生成事件信息，不生成行程的统计信息
 * @author web
 */
public class Algorithm0200ChainServiceImpl implements AlgorithmChainService {
    private static final Logger logger = LoggerFactory.getLogger(Algorithm0200ChainServiceImpl.class);
    /**
     * 最小速度阀值
     */
    private int speedMin;
    /**
     * 最大速度阀值
     */
    private int speedMax;
    /**
     * 引擎最小转速阀值
     */
    private int rotationMin;
    /**
     * 引擎最大转速阀值
     */
    private int rotationMax;
    /**
     * 缩放等级
     */
    private int zoomLevel;
    /**
     * 漏油高发区服务
     */
    private FuelLeakService fuelLeakService;
    /**
     * 算法管理器
     */
    private ArithmeticManager arithmeticManager;

    /**
     * 使用位置点0200数据进行的事件收集算法链<br/>
     * 只生成事件信息，不生成行程的统计信息
     */
    public Algorithm0200ChainServiceImpl() {
        rotationMax = ConfigMap.getInteger("rotation_max");
        rotationMin = ConfigMap.getInteger("rotation_min");
        speedMax = ConfigMap.getInteger("speed_max");
        speedMin = ConfigMap.getInteger("speed_min");
        zoomLevel = ConfigMap.getInteger("zoom_level");

        //初始化漏油高发地服务
        fuelLeakService = new FuelLeakServiceImpl();
        try {
            fuelLeakService.init(ConfigMap.getConf());
        } catch (Exception e) {
            logger.error("FuelLeakService初始化失败",e);
        }

        //构建算法链
        arithmeticManager = new ArithmeticManager()
            .addArithmetic(new Parking_0200())// 车辆停止
            .addArithmetic(new SpeedOver_0200())// 车辆超速
            .addArithmetic(new SpeedOverCurve_0200())// 弯道超速
            .addArithmetic(new FuelLeakLocation_0200());// 漏油高发地
    }

    /**
     * 执行方法
     * @param chainData 算法链的执行对象
     */
    @Override
    public void execute(AlgorithmChainData chainData) {
        List<PointProtocol> points = chainData.getPreTripInfo().getPoints0200();
        if (CommonUtils.isCollectionNotEmpty(points)) {
            DrivingData drivingData = chainData.getDrivingData();
            CommonData commonData = drivingData.getCommonData();

            for (int i=0,size=points.size(); i<size; i++) {
                PointProtocol point = points.get(i);

                //为本次计算设置公共数据
                if(i == 0){
                    commonData.clear();
                    commonData.setTid(point.getTid());
                    commonData.setTripFirstNode(true);
                }else if(i == size-1) {
                    commonData.setTripLastNode(true);
                }else{
                    commonData.setTripFirstNode(false);
                }

                handlePoint(chainData, point);

                //为下次计算设置数据
                if(i != size-1){
                    //为下次计算提供相关数据
                    commonData.setLastGpsTime(point.getGpsTime());
                    commonData.setLastRotation(point.getRotation());
                    commonData.setLastSpeedMs(commonData.getSpeedMs());
                    commonData.setLastGpsDirection(point.getDirection());
                }else{
                    //清空该算法链的所产生的中间缓存
                    drivingData.getCommonData().clear();
                    drivingData.getEventData().clear();
                    drivingData.getOuterEvent().clear();
                    Map<String, Object> statisticData = drivingData.getStatisticData();
                    arithmeticManager.getArithmeticList().stream().map(e -> e.getClass().getSimpleName()).forEach(statisticData::remove);
                }
            }
        }

        //设置算法链的输出缓存数据
        chainData.getOuterData().setOuterDrivingData(chainData.getDrivingData());
    }

    /**
     * 处理位置点数据
     * @param chainData   算法链执行的对象
     * @param point       点信息
     */
    private void handlePoint(AlgorithmChainData chainData, PointProtocol point) {
        DrivingData drivingData = chainData.getDrivingData();
        CommonData commonData = drivingData.getCommonData();
        long tid = point.getTid();
        long gpsTime = point.getGpsTime();
        int rotation = point.getRotation();
        int speed = (int) point.getSpeedKmh();

        //数据不合法跳过
        if (!validate(tid, commonData.getLastGpsTime(), gpsTime, rotation, speed)) {
            return;
        }

        //漏油高发地放入缓存
        if (speed == 0) {
            long tileId = TileUtils.getTileId(point.getOriginalLat(), point.getOriginalLng(), zoomLevel);
            FuelLeakLocation tile = fuelLeakService.get(tileId);
            if (tile!=null && tile.getTile_id()==0 && tile.getLat()==0 && tile.getLon()==0) {
                tile = null;
            }
            drivingData.setFuelLeakLocation(tile);// 可以为空
        }

        //算法链的公共缓存赋值
        TripStatisticUtils.assignData(point, commonData);

        //调用算法链计算相应事件
        arithmeticManager.setDrivingData(drivingData).calculate();

        //将算法链的输出事件列表放到统计结果的输出事件列表中
        String tripDay = DateUtils.formatMS(chainData.getPreTripInfo().getStartTime(), DateUtils.DateFormat.YYYYMMDD);
        drivingData.getOuterEvent().forEach(eventData ->{
            eventData.setTid(tid);
            eventData.setUid(tid);
            eventData.setDay(tripDay);
            eventData.setSType(ProtocolEnum.LOCATION.getType());
            chainData.getOuterData().getOuterEvents().add(eventData);
        });
        drivingData.getOuterEvent().clear();
    }

    /**
     * 校验数据合法性
     * @param tid             通信号
     * @param lastGpsTime     上个点时间
     * @param curGpsTime 当前点时间
     * @param rotation        转速
     * @param speed           速度
     * @return 是否合法
     */
    private boolean validate(long tid, long lastGpsTime, long curGpsTime, int rotation, int speed) {
        //本次gps时间小于等于上个点的gps时间，不予处理
        if (curGpsTime <= lastGpsTime) {
            logger.info("===本次gps时间小于等于上个点的gps时间，不予处理! (本次:{},上次：{}) 通信号：{} ===", curGpsTime, lastGpsTime, tid);
            return false;
        }

        //发动机转速不在合理区间内
        if (rotation < rotationMin || rotation > rotationMax) {
            logger.info("===发动机转速不在合理区间内，不予处理!（转速：" + rotation + "rpm）通信号：{} ===", tid);
            return false;
        }

        //速度不在合理区间内
        if (speed < speedMin || speed > speedMax) {
            logger.info("===速度不在合理区间内，不予处理!（速度：" + speed + "km/h）通信号：{} ===", tid);
            return false;
        }

        return true;
    }
}

